#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@Project ：django32_celery 
@File    ：configtest.py
@IDE     ：PyCharm 
@Author  ：Toby
@Date    ：2024/4/16 21:39 
'''

"""
scope:
    1、scope 默认是function，只要是函数都会运行
    2、class 类级别，每个函数都会运行一次
    3、session 整个作用域只运行一次
    4、module 整个py文件（模块）作用域只运行一次

autouse: 是否自动调用

"""
import requests
import json
import logging

logger = logging.getLogger(__name__)
import pytest
import configparser
from utilspakage.operation_excel import ExcelOp
from utilspakage.operation_yaml import Operation_Yaml

'''
conftest.py是什么？ conftest.py是fixture函数的一个集合，可以理解为公共的提取出来放在一个文件里，
然后供其它模块调用。不同于普通被调用的模块，conftest.py使用时不需要导入，Pytest会自动查找 

conftest.py使用场景 
如果我们有很多个前置函数，写在各个py文件中是不很乱？再或者说，我们很多个py文件想要使用同一个前置函数该怎么办？这也就是conftest.py的作用
'''
from utilspakage.writeconfig import CfgPar

driver = None

CONFIGPATH = "/root/django32_celery/pytest.ini"

Base_path = "/root/django32_celery/"
confg_path = Base_path + 'pytest.ini'
config_parm = configparser.ConfigParser()
config_parm.sections()
# 读取配置文件
config_parm.read(confg_path)

#  当pytest.fixture(scope="function")时，pytest的yield类似unittest的teardown。每个方法（函数）之前都会执行一次。
'''
fixture 的作用域：              fixture里面scope参数可以控制fixture的作用范围：session > module > class > function（默认）
@pytest.fixture() 可以用 scope 参数来指定其作用域：
例如：@pytest.fixture(scope='class')
注意：当 fixture 作用域为 function 时，且装饰在测试类上，那么这个测试类的每个方法都会调用执行一次这个 fixture；
function  ：fixture 默认的作用域，每个测试方法执行一次（如果被调用），默认时可以不用指定；
class      ：每个测试类执行一次，如果同一个测试类中多次调用该 fixture 也只会执行一次；
module   ：每个测试模块执行一次，如果同一个测试模块多次调用该 fixture 也只会执行一次；
session   ：每次测试会话中只执行一次，在同一次会话中多次调用，也只会执行一次；
说明：当 fixture 有返回值时，pytest 会把返回值缓存起来，如果 fixture 在指定的作用域内被多次调用，只有第一次调用会真正的被执行，后续调用会使用被缓存起来的返回值，而不是再执行一遍；
'''
file_path = "/root/django32_celery/media/" + CfgPar(CONFIGPATH).get_value('testchoose', 'scrlist')
data_file = "/root/django32_celery/media/test_yaml.yaml"
extract_yaml_path = "/root/django32_celery/pytest_package/extract.yaml"



# @pytest.fixture()
# def args():
#     logger.info(CfgPar(CONFIGPATH).get_value('testchoose', 'scrlist'))
#     logger.info("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
#     return ExcelOp(file_path).get_row_col_value()


# 通过参数传递到args中  方式一  OK
# @pytest.fixture(params=ExcelOp(file_path).get_row_col_value(), autouse=True, scope='session')
# def args(request):
#     # rs = ExcelOp(file_path).get_row_col_value()
#     logger.info(CfgPar(CONFIGPATH).get_value('testchoose', 'scrlist'))
#     logger.info(f"￥￥￥￥￥￥￥￥￥￥￥￥￥:{request.param}￥￥￥￥￥￥￥￥￥￥￥")
#     yield request.param

# fixture函数的 params 请求参数数量（请求参数的数据类型为列表/元组，请求参数数量为列表/元组元素个数）决定fixture函数执行的次数
#此时fixture函数的装饰器 @pytest.fixture(params=test_data) 参数不能忘记传值。

def yaml_file():
    logger.info("读取yaml文件++++++++++++++++++++++++++++++++++++++++++++++++++++")
    return Operation_Yaml.read_yaml(file_path)

# OK
@pytest.fixture(params=yaml_file(),scope='module')
def data_info(request):
    logger.info(f"^^^^^^^^^^^^^^^^^^:{request.param}^^^^^^^^^^^^^^^^^^:")
    yield request.param


@pytest.fixture(scope='class')
def get_token_fix():
    '''
    作用域  session ，返回token
    :return:
    '''
    headers = {
        'Content-Type': 'multipart/form-data',
        'Connection': 'keep-alive',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/88.0.4324.104 Safari/537.36'
    }
    _env = config_parm['token']['env']
    _url = config_parm['token']['url']
    url = _env + _url
    print(url)

    _data = {'mobile': config_parm['token']['mobile'], 'openid': config_parm['token']['openid'],
             'unionid': config_parm['token']['unionid']
             }
    res = requests.get(url=url, headers=headers, params=_data).text
    logger.info(res)
    res = json.loads(res)
    extract_dt = {
        'sid': res['data']['sid'],
        'token': res['data']['token']
    }

    print(extract_dt)
    logger.info(extract_dt)

    Operation_Yaml.write_yaml(extract_yaml_path,extract_dt)

    return extract_dt


# 钩子方法：pytest_runtest_makereport ，可以更清晰的了解测试用例的执行过程，并获取到每个测试用例的执行结果。
# 装饰器 pytest.hookimpl(hookwrapper=True, tryfirst=True) 解释：
#  @pytest.hookimpl(hookwrapper=True) 装饰的钩子函数，有以下两个作用：
# 1、可以获取到测试用例不同执行阶段的结果（setup，call，teardown）
# 2、可以获取钩子方法 pytest_runtest_makereport(item, call) 的调用结果（yield返回一个测试用例执行后的result对象）和调用结果result对象中的测试报告（返回一个report对象）
#  pytest_runtest_makereport(item, call) 钩子函数参数解释：
# 1、 item 是测试用例对象；
# 2、 call 是测试用例的测试步骤；具体执行过程如下：
# ①先执行  when="setup" ，返回setup用例前置操作函数的执行结果。
# ②然后执行  when="call" ，返回call测试用例的执行结果。
# ③最后执行  when="teardown"  ，返回teardown用例后置操作函数的执行结果。

@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
    print('---------------pytest_runtest_makereport---------------------')
    # 获取钩子方法的调用结果
    out = yield
    # logger.info('钩子执行结果:', out)
    # 从钩子方法的调用结果中获取测试报告
    report = out.get_result()

    if report.when == "call":
        print('当前用例执行步骤：%s' % report.when)
        print('当前用例的nodeid：%s' % report.nodeid)
        print('description:%s' % str(item.function.__doc__))
        print(('执行结果: %s' % report.outcome))


@pytest.fixture(scope="session", autouse=True)
def fix_a():
    print("setup 前置操作")
    yield
    print("teardown 后置操作")


def pytest_unconfigure(config):  # pytest 中常见的hooks ，在所有测试用例执行完毕之后调用，可用来执行清理工作
    print("所有测试已经完成，现在可以进行一些清理工作,发送测试结果到邮箱中通知各位本次的测试结果。")





# 更改测试报告的标题
def pytest_html_report_title(report):
    report.title = "接口自动化测试报告"



